#include <stdio.h>
#include <malloc.h>

#include "RaidConf.h"
#include "MapSector.h"
#include "TestMap.h"

/* 
 * Find first set bit in the number.
 * Undefined if the number is 0. The caller should check this.
 */
static int find_first_set_bit(int num){
	int index = 0;
	if ((num & 0xffff) == 0) {
		index += 16;
		num >>= 16;
	}
	if ((num & 0xff) == 0) {
		index += 8;
		num >>= 8;
	}
	if ((num & 0xf) == 0) {
		index += 4;
		num >>= 4;
	}
	if((num & 3) == 0) {
		index += 2;
		num >>= 2;
	}
	if((num & 1) == 0) {
		index += 1;
	}
	return index;
}


static void test_raid456 (void)
{
	raid5_conf_t conf;
	sector_info_t s_info;
	int algo, max_algo;
	int raid_level;
	int raid_disks, data_disks;
	int chunk_sectors;
	sector_t log_sec, sector;
	int counter = 0;
	int error = 0;

	for (raid_level = RAID4; raid_level <= RAID6; raid_level++) {
		printf("Testing RAID%d\n", raid_level);
		conf.level = raid_level;
		max_algo = (raid_level == RAID5) ? ALGORITHM_PARITY_N : ALGORITHM_PARITY_0_6;
		max_algo = (raid_level == 4) ? 0 : max_algo;
	
		for (algo = 0; (algo <= max_algo) && !error; algo++) {
			conf.algorithm = algo;
			raid_disks = MIN_RAID_DISKS;
			data_disks = raid_disks-1;
			if (raid_level == RAID6)
				data_disks -= 1;
	
			for (; (raid_disks <= MAX_RAID_DISKS) && !error; raid_disks++, data_disks++) {
				conf.raid_disks = raid_disks;
				conf.data_disks = data_disks;
				for (chunk_sectors = SECPERCHUNK; (chunk_sectors <= MAX_SECPERCHUNK) && !error; chunk_sectors *= 2) {
					conf.chunk_sectors = chunk_sectors;
					for (log_sec = FIRST_LOGICAL_SEC; (log_sec <= MAX_LOGICAL_SEC) && !error; log_sec ++) {
						counter++;

						raid456_find_phys(&conf, log_sec, &s_info);
						sector = raid456_find_virt(&conf, &s_info);

						/*
						printf("RAID%d, log_sec:%d, phy_sec:%d, disknum:%d, rev_map:%d, algo:%d," \
									"data disks: %d raid disks:%d \n", raid_level, log_sec, s_info.sector, \
									s_info.disknum, sector, algo, data_disks, raid_disks);
						*/
						
						if (sector != log_sec) {
							error = 1;
							printf ("ERROR: logical sector: %d, reverse mapped sector: %d\n", \
												log_sec, sector);
							
							printf("RAID%d, log_sec:%d, phy_sec:%d, disknum:%d, rev_map:%d, algo:%d, \
										data disks: %d raid disks:%d \n", raid_level, log_sec, s_info.sector, \
										s_info.disknum, sector, algo, data_disks, raid_disks);
							
						}
					}
				}
			}
			if (!error && (raid_level != RAID4))
				printf("Success for algorithm: %d\n", algo);
		}
		if (!error)
			printf ("Success for RAID%d\n\n", raid_level);
	}
	printf("Number of iterations: %d\n", counter);
}

static void test_raid10 (void)
{
	raid10_conf_t	conf;
	r10_sector_info_t *r10_s_info;
	sector_info_t s_info;
	int near_copies, far_copies, offset;
	int chunk_sectors;
	sector_t log_sec, sector;
	int error = 0;
	int counter = 0;
	int copy = 0;

	printf("Testing RAID10\n");

	for (near_copies = MIN_NEAR_COPIES; !error && (near_copies <= MAX_NEAR_COPIES); near_copies *= 2) {
		conf.near_copies = near_copies;
		
		for (far_copies = MIN_FAR_COPIES; !error && (far_copies <= MAX_FAR_COPIES); far_copies*=2) {
			conf.far_copies = far_copies;
			conf.copies = near_copies * far_copies;
			conf.raid_disks = near_copies * far_copies;
			r10_s_info = (r10_sector_info_t *)malloc (conf.raid_disks * sizeof(sector_info_t));

			if (r10_s_info == NULL) {
				printf ("%s: ERROR: Out of Memory\n", __FUNCTION__);
				error = 1;
				break;
			}
			for (chunk_sectors = SECPERCHUNK; !error && (chunk_sectors <= MAX_SECPERCHUNK); chunk_sectors *= 2) {
				conf.chunk_shift = find_first_set_bit(chunk_sectors);
				conf.chunk_mask = chunk_sectors - 1;
				for (offset = 0; !error && (offset <= 1); offset++) {
					conf.far_offset = offset;
	
					if (offset)
						conf.stride = 1 << conf.chunk_shift;
					else
						conf.stride = MAX_DISK_SECTORS / far_copies;
					for (log_sec = FIRST_LOGICAL_SEC; !error && (log_sec < MAX_LOGICAL_SEC); log_sec ++) {
						counter++;
						raid10_find_phys(&conf, log_sec, r10_s_info);
						
						for (copy = 0; copy < conf.copies; copy++) {
							s_info.sector = r10_s_info->devs[copy].sector;
							s_info.disknum = r10_s_info->devs[copy].disknum;
							/*
							printf("RAID10: log_sec:%d, phy_sec:%d, disknum:%d, far_offset: %d," \
										"near_copies: %d, far_copies: %d, raid_disks:%d stride: %d " \
										"chunk_sectors: %d\n", log_sec, s_info.sector, s_info.disknum, offset,\
										near_copies, far_copies, conf.raid_disks, conf.stride, chunk_sectors);
							*/							
							sector = raid10_find_virt(&conf, &s_info);
						
							if (sector != log_sec) {
								printf ("ERROR:logical sector no:%d, reverse mapped sector:%d\n", log_sec, sector);
								printf("RAID10: log_sec:%d, phy_sec:%d, disknum:%d, far_offset: %d," \
											"near_copies: %d, far_copies: %d, raid_disks:%d stride: %d " \
										"chunk_sectors: %d\n", log_sec, s_info.sector, s_info.disknum, offset,\
										near_copies, far_copies, conf.raid_disks, conf.stride, chunk_sectors);
								error = 1;
							}
						}
					}
					if (!error)
						printf ("success for near copies:%d, far cpoies:%d, chunk size:%d, far offset:%d\n", near_copies, far_copies, chunk_sectors, offset);
				}
			}
			free (r10_s_info);
		}
	}
	printf("Number of Iterations: %d\n", counter);
}

int main ()
{
	test_raid456();
	test_raid10();

	return 0;
}


